using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Autodesk.Revit.UI;
using Autodesk.Revit.DB;
using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Linq;
using Autodesk.Revit.UI.Selection;

namespace CodRevit.ViewModels
{
    public partial class ScatterFamilyViewModel : ObservableObject
    {
        private readonly UIApplication _uiapp;
        private readonly Document _doc;
        private readonly Window _window;
        private readonly ExternalEvent _externalEvent;
        private readonly ScatterFamilyHandler _scatterHandler;
        
        [ObservableProperty]
        private ObservableCollection<FamilySymbol> _familySymbols;
        [ObservableProperty]
        private FamilySymbol _selectedFamilySymbol;
        [ObservableProperty]
        private int _count = 10;
        [ObservableProperty]
        private double _minRotationX = 0;
        [ObservableProperty]
        private double _maxRotationX = 360;
        [ObservableProperty]
        private string _status = "请选择族类型和楼板";

        public ScatterFamilyViewModel(UIApplication uiapp, Window window, ExternalEvent externalEvent, ScatterFamilyHandler scatterHandler)
        {
            _uiapp = uiapp;
            _doc = uiapp.ActiveUIDocument.Document;
            _window = window;
            _externalEvent = externalEvent;
            _scatterHandler = scatterHandler;

            // 获取所有常规模型族类型
            FamilySymbols = new ObservableCollection<FamilySymbol>(
                new FilteredElementCollector(_doc)
                    .OfClass(typeof(FamilySymbol))
                    .OfCategory(BuiltInCategory.OST_GenericModel)
                    .Cast<FamilySymbol>()
                    .OrderBy(f => f.Family.Name)
                    .ThenBy(f => f.Name));

            // 设置默认选择第一个族类型
            if (FamilySymbols.Any())
            {
                SelectedFamilySymbol = FamilySymbols.First();
                Status = "请选择楼板";
            }
            else
            {
                Status = "未找到可用的族类型";
            }
        }

        [RelayCommand]
        private void PickFloor()
        {
            if (SelectedFamilySymbol == null)
            {
                Status = "请先选择族类型";
                return;
            }
            try
            {
                _window.Hide();
                Reference reference = _uiapp.ActiveUIDocument.Selection.PickObject(
                    ObjectType.Element, new FloorSelectionFilter(), "请选择楼板");
                Floor? floor = _doc.GetElement(reference.ElementId) as Floor;
                if (floor != null)
                {
                    _scatterHandler.SetParameters(floor, SelectedFamilySymbol, Count, MinRotationX, MaxRotationX,
                        message => Status = message);
                    _externalEvent.Raise();
                }
            }
            catch (Autodesk.Revit.Exceptions.OperationCanceledException)
            {
                // 一定要记得处理用户取消选择的异常
                Status = "操作已取消";
            }
            catch (Exception ex)
            {
                Status = $"错误：{ex.Message}";
            }
            finally
            {
                //finally是无论如何都会执行的，重新显示窗口并且激活
                _window.Show();
                _window.Activate();
            }
        }
    }

    public class FloorSelectionFilter : ISelectionFilter
    {
        public bool AllowElement(Element elem)
        {
            return elem is Floor;
        }
        public bool AllowReference(Reference reference, XYZ position)
        {
            return false;
        }
    }
    //只是定义在了vm中，事实上放在哪里都行
    public class ScatterFamilyHandler : IExternalEventHandler
    {
        private readonly Document _doc;
        private Floor _floor;
        private FamilySymbol _familySymbol;
        private int _count;
        private double _minRotationX;
        private double _maxRotationX;
        //这个委托会在vm中被赋值，作用是将EventHandler状态信息反馈到vm中
        private Action<string> _statusCallback;
        private Random _random = new Random();

        public ScatterFamilyHandler(Document doc) => _doc = doc;
        public void SetParameters(Floor floor, FamilySymbol familySymbol, int count,
            double minRotationX, double maxRotationX, Action<string> statusCallback)
        {
            _floor = floor;
            _familySymbol = familySymbol;
            _count = count;
            _minRotationX = minRotationX * Math.PI / 180.0;
            _maxRotationX = maxRotationX * Math.PI / 180.0;
            _statusCallback = statusCallback;
        }
        public void Execute(UIApplication app)
        {
            if (_floor == null || _familySymbol == null)
            {
                _statusCallback?.Invoke("错误：未设置楼板或族类型");
                return;
            }
            try
            {
                using (Transaction trans = new Transaction(_doc, "散布族实例"))
                {
                    trans.Start();
                    // 确保族类型已激活
                    if (!_familySymbol.IsActive)
                    {
                        _familySymbol.Activate();
                    }
                    // 获取楼板的包围盒边界
                    var floorGeometry = _floor.get_Geometry(new Options());
                    var boundingBox = floorGeometry.GetBoundingBox();
                    var minPoint = boundingBox.Min;
                    var maxPoint = boundingBox.Max;

                    int successCount = 0;
                    int attempts = 0;
                    // 最大尝试次数
                    // 为什么会有最大尝试次数呢？
                    // 因为在某些情况下，随机生成的点可能不在楼板上，导致无法创建族实例。
                    // 因此，为了确保放置的数量能够达到_count，需要多次尝试，过滤掉不在楼板上的点。
                    int maxAttempts = _count * 3;

                    while (successCount < _count && attempts < maxAttempts)
                    {
                        attempts++;
                        // 生成随机点
                        double x = _random.NextDouble() * (maxPoint.X - minPoint.X) + minPoint.X;
                        double y = _random.NextDouble() * (maxPoint.Y - minPoint.Y) + minPoint.Y;
                        XYZ point = new XYZ(x, y, 0);
                        // 检查点是否在楼板内
                        if (!IsPointOnFloor(_floor, point))
                        {
                            continue;
                        }
                        // 获取该点的Z坐标
                        double z = GetFloorZCoordinate(_floor, point);
                        point = new XYZ(x, y, z);
                        // 创建族实例
                        try
                        {
                            // 生成随机旋转角度（只保留X轴）
                            double rotationX = _random.NextDouble() * (_maxRotationX - _minRotationX) + _minRotationX;
                            // 创建族实例(放置点，族类型，宿主元素，非结构类型)
                            FamilyInstance instance = _doc.Create.NewFamilyInstance(point, _familySymbol, _floor,
                                Autodesk.Revit.DB.Structure.StructuralType.NonStructural);
                            try
                            {
                                // 获取族实例的位置
                                LocationPoint location = instance.Location as LocationPoint;
                                if (location != null)
                                {
                                    // 使用Location的Rotate方法进行旋转
                                    location.Rotate(Line.CreateBound(point, point.Add(XYZ.BasisZ)), rotationX);
                                }
                                else
                                {
                                    _statusCallback?.Invoke("警告：无法获取族实例位置点");
                                }
                            }
                            catch
                            {
                                // 如果旋转失败，至少保留创建的实例
                                _statusCallback?.Invoke("警告：某些实例未能按预期旋转");
                            }

                            successCount++;
                        }
                        catch (Exception ex)
                        {
                            _statusCallback?.Invoke($"放置族实例时出错: {ex.Message}");
                            continue;
                        }
                    }
                    trans.Commit();
                    _statusCallback?.Invoke($"成功放置 {successCount} 个族实例");
                }
            }
            catch (Exception ex)
            {
                _statusCallback?.Invoke($"执行失败: {ex.Message}");
            }
        }
        //没啥好说的，常规的解析几何模型的方式
        /// <summary>
        /// 检查给定点是否在楼板上
        /// </summary>
        /// <param name="floor">要检查的楼板</param>
        /// <param name="point">要检查的点</param>
        /// <returns>如果点在楼板上返回true,否则返回false</returns>
        private bool IsPointOnFloor(Floor floor, XYZ point)
        {
            // 参数检查
            if (floor == null || point == null)
                return false;
            try
            {
                // 创建几何选项,启用引用计算和精细详细程度
                Options? options = new Options
                {
                    ComputeReferences = true,
                    DetailLevel = ViewDetailLevel.Fine
                };
                // 获取楼板的几何数据
                GeometryElement? geometry = floor.get_Geometry(options);
                if (geometry == null)
                    return false;

                // 遍历几何对象
                foreach (GeometryObject obj in geometry)
                {
                    if (obj == null) continue;
                    // 尝试获取实体
                    Solid? solid = null;
                    if (obj is Solid s)
                    {
                        // 如果对象本身就是实体,直接使用
                        solid = s;
                    }
                    else if (obj is GeometryInstance gi)
                    {
                        // 如果是几何实例,获取其实例几何
                        GeometryElement? instanceGeometry = gi.GetInstanceGeometry();
                        if (instanceGeometry != null)
                        {
                            // 遍历实例几何中的对象,找到第一个有效的实体
                            foreach (GeometryObject instanceObj in instanceGeometry)
                            {
                                if (instanceObj is Solid s2 && s2.Faces.Size > 0)
                                {
                                    solid = s2;
                                    break;
                                }
                            }
                        }
                    }

                    // 如果找到了有效的实体,处理其面
                    if (solid != null && solid.Faces.Size > 0)
                    {
                        // 遍历实体的所有面
                        foreach (Face face in solid.Faces)
                        {
                            if (face is PlanarFace planarFace)
                            {
                                // 检查是否是顶面（法线朝上）
                                if (planarFace.FaceNormal.IsAlmostEqualTo(XYZ.BasisZ))
                                {
                                    try
                                    {
                                        // 将点投影到面上
                                        IntersectionResult? projResult = planarFace.Project(point);
                                        if (projResult == null) continue;
                                        UV? uv = projResult.UVPoint;
                                        if (uv == null) continue;
                                        // 检查投影点是否在面的边界内
                                        return planarFace.IsInside(uv);
                                    }
                                    catch
                                    {
                                        // 如果投影或检查失败,继续检查下一个面
                                        continue;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                // 记录错误但继续执行
                _statusCallback?.Invoke($"检查点位时出错: {ex.Message}");
            }

            // 如果所有检查都失败,返回false
            return false;
        }

        private double GetFloorZCoordinate(Floor floor, XYZ point)
        {
            if (floor == null || point == null)
                return 0;
            try
            {
                var options = new Options
                {
                    ComputeReferences = true,
                    DetailLevel = ViewDetailLevel.Fine
                };
                var geometry = floor.get_Geometry(options);
                if (geometry == null)
                    return 0;
                foreach (var obj in geometry)
                {
                    if (obj == null) continue;
                    // 尝试获取实体
                    Solid? solid = null;
                    if (obj is Solid s)
                    {
                        solid = s;
                    }
                    else if (obj is GeometryInstance gi)
                    {
                        var instanceGeometry = gi.GetInstanceGeometry();
                        if (instanceGeometry != null)
                        {
                            foreach (var instanceObj in instanceGeometry)
                            {
                                if (instanceObj is Solid s2 && s2.Faces.Size > 0)
                                {
                                    solid = s2;
                                    break;
                                }
                            }
                        }
                    }
                    if (solid != null && solid.Faces.Size > 0)
                    {
                        foreach (Face face in solid.Faces)
                        {
                            if (face is PlanarFace planarFace &&
                                planarFace.FaceNormal.IsAlmostEqualTo(XYZ.BasisZ))
                            {
                                return planarFace.Origin.Z;
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                _statusCallback?.Invoke($"获取高度时出错: {ex.Message}");
            }

            return 0;
        }

        public string GetName()
        {
            return "ScatterFamilyCreator";
        }
    }
}